package org.apache.maven.plugin.deploy;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.deployer.ArtifactDeploymentException;
import org.apache.maven.artifact.metadata.ArtifactMetadata;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.project.artifact.ProjectArtifactMetadata;
import org.apache.maven.project.validation.ModelValidationResult;
import org.apache.maven.project.validation.ModelValidator;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.WriterFactory;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * Installs the artifact in the remote repository.
 *
 * @author <a href="mailto:aramirez@apache.org">Allan Ramirez</a>
 */
@Mojo(name = "verify", requiresProject = false, threadSafe = true)
public class DeployFileMojo
        extends AbstractDeployMojo {

    /**
     * The default Maven project created when building the plugin
     */
    @Parameter(defaultValue = "${project}", readonly = true, required = true)
    private MavenProject project;

    /**
     * Used for attaching the source and javadoc jars to the project.
     */
    @Component
    private MavenProjectHelper projectHelper;

    /**
     * GroupId of the artifact to be deployed. Retrieved from POM file if
     * specified.
     */
    @Parameter(property = "groupId", defaultValue = "com.cave")
    private String groupId;

    /**
     * ArtifactId of the artifact to be deployed. Retrieved from POM file if
     * specified.
     */
//    @Parameter(property = "artifactId")
//    private String artifactId;
    /**
     * Version of the artifact to be deployed. Retrieved from POM file if
     * specified.
     */
    @Parameter(property = "version", defaultValue = "1.0.0")
    private String version;

    /**
     * Type of the artifact to be deployed. Retrieved from the &lt;packaging&gt
     * element of the POM file if a POM file specified. Defaults to the file
     * extension if it is not specified via command line or POM.<br/>
     * Maven uses two terms to refer to this datum: the &lt;packaging&gt;
     * element for the entire POM, and the &lt;type&gt; element in a dependency
     * specification.
     */
    @Parameter(property = "packaging", defaultValue = "jar")
    private String packaging;

    /**
     * Description passed to a generated POM file (in case of generatePom=true)
     */
    @Parameter(property = "generatePom.description")
    private String description;

    /**
     * File to be deployed.
     */
//    @Parameter(property = "file", required = true)
//    private File file;
    /**
     * The bundled API docs for the artifact.
     *
     * @since 2.6
     */
    @Parameter(property = "javadoc")
    private File javadoc;

    /**
     * The bundled sources for the artifact.
     *
     * @since 2.6
     */
    @Parameter(property = "sources")
    private File sources;

    /**
     * Server Id to map on the &lt;id&gt; under &lt;server&gt; section of
     * settings.xml In most cases, this parameter will be required for
     * authentication.
     */
    @Parameter(property = "repositoryId", defaultValue = "thirdparty", required = true)
    private String repositoryId;

    /**
     * The type of remote repository layout to deploy to. Try <i>legacy</i> for
     * a Maven 1.x-style repository layout.
     */
    @Parameter(property = "repositoryLayout", defaultValue = "default")
    private String repositoryLayout;

    /**
     * URL where the artifact will be deployed. <br/>
     * ie ( file:///C:/m2-repo or scp://host.com/path/to/repo )
     */
    @Parameter(property = "url", required = true, defaultValue = "http://localhost:8081/nexus/content/repositories/thirdparty")
    private String url;

    /**
     * Location of an existing POM file to be deployed alongside the main
     * artifact, given by the ${file} parameter.
     */
    @Parameter(property = "pomFile")
    private File pomFile;

    /**
     * Upload a POM for this artifact. Will generate a default POM if none is
     * supplied with the pomFile argument.
     */
    @Parameter(property = "generatePom", defaultValue = "true")
    private boolean generatePom;

    /**
     * Add classifier to the artifact
     */
    @Parameter(property = "classifier")
    private String classifier;

    /**
     * Whether to deploy snapshots with a unique version or not.
     */
    @Parameter(property = "uniqueVersion", defaultValue = "true")
    private boolean uniqueVersion;

    /**
     * The component used to validate the user-supplied artifact coordinates.
     */
    @Component
    private ModelValidator modelValidator;

    /**
     * A comma separated list of types for each of the extra side artifacts to
     * deploy. If there is a mis-match in the number of entries in
     * {@link #files} or {@link #classifiers}, then an error will be raised.
     */
    @Parameter(property = "types")
    private String types;

    /**
     * A comma separated list of classifiers for each of the extra side
     * artifacts to deploy. If there is a mis-match in the number of entries in
     * {@link #files} or {@link #types}, then an error will be raised.
     */
    @Parameter(property = "classifiers")
    private String classifiers;

    /**
     * A comma separated list of files for each of the extra side artifacts to
     * deploy. If there is a mis-match in the number of entries in
     * {@link #types} or {@link #classifiers}, then an error will be raised.
     */
    @Parameter(property = "files")
    private String files;

    // For cave Variable
    private Vector<String> filePaths;
    private String projectName;
    private File[] libFiles;
    private String[] artifactIds;

    private final String antFileName = "build.xml";

    void initProperties()
            throws MojoExecutionException {
        // Process the supplied POM (if there is one)
        if (pomFile != null) {
            generatePom = false;

            Model model = readModel(pomFile);

            //processModel(model);
        }

//        if (packaging == null && file != null) {
//            packaging = FileUtils.getExtension(file.getName());
//        }
    }

    private void analyzeAntFile() throws MojoExecutionException {
        File antFile = new File(antFileName);
        if (!antFile.exists()) {
            throw new MojoExecutionException(" Ant build file not found.");
        }

        if (filePaths == null) {
            filePaths = new Vector<String>();
        }

        try {
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder;
            dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(antFile);

            doc.getDocumentElement().normalize();

            Element project = doc.getDocumentElement();
            projectName = project.getAttribute("name");

//            System.out.println(" Proejct Name is " + projectName);
            NodeList jarList = doc.getElementsByTagName("pathelement");

            for (int i = 0; i < jarList.getLength(); ++i) {
                Element e = (Element) jarList.item(i);
                String path = e.getAttribute("location");
                if (path.contains(".jar")) {
                    filePaths.add(path);
                }

            }

            libFiles = new File[filePaths.size()];
            artifactIds = new String[filePaths.size()];

            for (int i = 0; i < filePaths.size(); i++) {

                String fps = filePaths.get(i);
//                System.out.println("File Path : " + fps);

                if (!fps.contains(":")) {
                    fps = "./" + fps;
                }

                libFiles[i] = new File(fps);

                if (!libFiles[i].exists()) {
                    throw new MojoExecutionException("File Path : " + fps + "is not exist");
                }

                int fpos = fps.lastIndexOf("/");
                int lpos = fps.lastIndexOf(".");
                fps = fps.substring(fpos + 1, lpos);
                fps = fps.toLowerCase();
                artifactIds[i] = fps;

            }

            NodeList projectList = project.getElementsByTagName("cave");

            if (projectList.getLength() == 0) {
                Element cave = doc.createElement("cave");

                for (int i = 0; i < filePaths.size(); i++) {
                    Element artifact = doc.createElement("artifact");
                    artifact.appendChild(doc.createTextNode(artifactIds[i]));
                    cave.appendChild(artifact);
                }

                project.appendChild(cave);

                TransformerFactory factory = TransformerFactory.newInstance();
                Transformer transformer = factory.newTransformer();

                DOMSource source = new DOMSource(doc);

                StreamResult result = new StreamResult(antFile);

                transformer.transform(source, result);
            }

        } catch (ParserConfigurationException ex) {
            Logger.getLogger(DeployFileMojo.class.getName()).log(Level.SEVERE, "Parser Error", ex);
        } catch (SAXException ex) {
            Logger.getLogger(DeployFileMojo.class.getName()).log(Level.SEVERE, "SAX ERROr", ex);
        } catch (IOException ex) {
            Logger.getLogger(DeployFileMojo.class.getName()).log(Level.SEVERE, "IO ERROR", ex);
        } catch (TransformerConfigurationException ex) {
            Logger.getLogger(DeployFileMojo.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerException ex) {
            Logger.getLogger(DeployFileMojo.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private void doDeploy() throws MojoExecutionException, MojoFailureException {
        failIfOffline();

//        if (!file.exists()) {
//            throw new MojoExecutionException(file.getPath() + " not found.");
//        }
        initProperties();

        ArtifactRepositoryLayout layout = getLayout(repositoryLayout);

        ArtifactRepository deploymentRepository
                = repositoryFactory.createDeploymentArtifactRepository(repositoryId, url, layout, uniqueVersion);

        String protocol = deploymentRepository.getProtocol();

        if (StringUtils.isEmpty(protocol)) {
            throw new MojoExecutionException("No transfer protocol found.");
        }
        analyzeAntFile();
        // Create the artifact
        Artifact[] artifacts = new Artifact[libFiles.length];

        for (int i = 0; i < libFiles.length; i++) {

            validateArtifactInformation(artifactIds[i]);

            artifacts[i] = artifactFactory.createArtifactWithClassifier(groupId, artifactIds[i], version, packaging, classifier);
            if (libFiles[i].equals(getLocalRepoFile(artifacts[i]))) {
                throw new MojoFailureException("Cannot deploy artifact from the local repository: " + libFiles[i]);
            }

            // Upload the POM if requested, generating one if need be
            if (!"pom".equals(packaging)) {
                if (pomFile != null) {
                    ArtifactMetadata metadata = new ProjectArtifactMetadata(artifacts[i], pomFile);
                    artifacts[i].addMetadata(metadata);
                } else if (generatePom) {
                    ArtifactMetadata metadata = new ProjectArtifactMetadata(artifacts[i], generatePomFile(artifactIds[i]));
                    artifacts[i].addMetadata(metadata);
                }
            }

            if (updateReleaseInfo) {
                artifacts[i].setRelease(true);
            }

            project.setArtifact(artifacts[i]);
        }

        for (int i = 0; i < filePaths.size(); i++) {
            try {
                deploy(libFiles[i], artifacts[i], deploymentRepository, getLocalRepository(), getRetryFailedDeploymentCount());
            } catch (ArtifactDeploymentException e) {
                throw new MojoExecutionException(e.getMessage(), e);
            }
        }

//        if ( sources != null )
//        {
//            projectHelper.attachArtifact( project, "jar", "sources", sources );
//        }
//
//        if ( javadoc != null )
//        {
//            projectHelper.attachArtifact( project, "jar", "javadoc", javadoc );
//        }
//
//        if ( files != null )
//        {
//            if ( types == null )
//            {
//                throw new MojoExecutionException( "You must specify 'types' if you specify 'files'" );
//            }
//            if ( classifiers == null )
//            {
//                throw new MojoExecutionException( "You must specify 'classifiers' if you specify 'files'" );
//            }
//            int filesLength = StringUtils.countMatches( files, "," );
//            int typesLength = StringUtils.countMatches( types, "," );
//            int classifiersLength = StringUtils.countMatches( classifiers, "," );
//            if ( typesLength != filesLength )
//            {
//                throw new MojoExecutionException( "You must specify the same number of entries in 'files' and "
//                    + "'types' (respectively " + filesLength + " and " + typesLength + " entries )" );
//            }
//            if ( classifiersLength != filesLength )
//            {
//                throw new MojoExecutionException( "You must specify the same number of entries in 'files' and "
//                    + "'classifiers' (respectively " + filesLength + " and " + classifiersLength + " entries )" );
//            }
//            int fi = 0;
//            int ti = 0;
//            int ci = 0;
//            for ( int i = 0; i <= filesLength; i++ )
//            {
//                int nfi = files.indexOf( ',', fi );
//                if ( nfi == -1 )
//                {
//                    nfi = files.length();
//                }
//                int nti = types.indexOf( ',', ti );
//                if ( nti == -1 )
//                {
//                    nti = types.length();
//                }
//                int nci = classifiers.indexOf( ',', ci );
//                if ( nci == -1 )
//                {
//                    nci = classifiers.length();
//                }
//                File file = new File( files.substring( fi, nfi ) );
//                if ( !file.isFile() )
//                {
//                    // try relative to the project basedir just in case
//                    file = new File( project.getBasedir(), files.substring( fi, nfi ) );
//                }
//                if ( file.isFile() )
//                {
//                    if ( StringUtils.isWhitespace( classifiers.substring( ci, nci ) ) )
//                    {
//                        projectHelper.attachArtifact( project, types.substring( ti, nti ).trim(), file );
//                    }
//                    else
//                    {
//                        projectHelper.attachArtifact( project, types.substring( ti, nti ).trim(),
//                                                      classifiers.substring( ci, nci ).trim(), file );
//                    }
//                }
//                else
//                {
//                    throw new MojoExecutionException( "Specified side artifact " + file + " does not exist" );
//                }
//                fi = nfi + 1;
//                ti = nti + 1;
//                ci = nci + 1;
//            }
//        }
//        else
//        {
//            if ( types != null )
//            {
//                throw new MojoExecutionException( "You must specify 'files' if you specify 'types'" );
//            }
//            if ( classifiers != null )
//            {
//                throw new MojoExecutionException( "You must specify 'files' if you specify 'classifiers'" );
//            }
//        }
//        @SuppressWarnings( "unchecked" )
//        List<Artifact> attachedArtifacts = project.getAttachedArtifacts();
//
//        for ( Artifact attached : attachedArtifacts )
//        {
//            try
//            {
//                deploy( attached.getFile(), attached, deploymentRepository, getLocalRepository(), getRetryFailedDeploymentCount() );
//            }
//            catch ( ArtifactDeploymentException e )
//            {
//                throw new MojoExecutionException( "Error deploying attached artifact " + attached.getFile() + ": "
//                    + e.getMessage(), e );
//            }
//        }
    }

    public void execute()
            throws MojoExecutionException, MojoFailureException {
        doDeploy();
    }

    /**
     * Gets the path of the specified artifact within the local repository. Note
     * that the returned path need not exist (yet).
     *
     * @param artifact The artifact whose local repo path should be determined,
     * must not be <code>null</code>.
     * @return The absolute path to the artifact when installed, never
     * <code>null</code>.
     */
    private File getLocalRepoFile(Artifact artifact) {
        String path = getLocalRepository().pathOf(artifact);
        return new File(getLocalRepository().getBasedir(), path);
    }

    /**
     * Process the supplied pomFile to get groupId, artifactId, version, and
     * packaging
     *
     * @param model The POM to extract missing artifact coordinates from, must
     * not be <code>null</code>.
     */
//    private void processModel(Model model) {
//        Parent parent = model.getParent();
//
//        if (this.groupId == null) {
//            this.groupId = model.getGroupId();
//            if (this.groupId == null && parent != null) {
//                this.groupId = parent.getGroupId();
//            }
//        }
//        if (this.artifactId == null) {
//            this.artifactId = model.getArtifactId();
//        }
//        if (this.version == null) {
//            this.version = model.getVersion();
//            if (this.version == null && parent != null) {
//                this.version = parent.getVersion();
//            }
//        }
//        if (this.packaging == null) {
//            this.packaging = model.getPackaging();
//        }
//    }
    /**
     * Extract the model from the specified POM file.
     *
     * @param pomFile The path of the POM file to parse, must not be
     * <code>null</code>.
     * @return The model from the POM file, never <code>null</code>.
     * @throws MojoExecutionException If the file doesn't exist of cannot be
     * read.
     */
    Model readModel(File pomFile)
            throws MojoExecutionException {
        Reader reader = null;
        try {
            reader = ReaderFactory.newXmlReader(pomFile);
            return new MavenXpp3Reader().read(reader);
        } catch (FileNotFoundException e) {
            throw new MojoExecutionException("POM not found " + pomFile, e);
        } catch (IOException e) {
            throw new MojoExecutionException("Error reading POM " + pomFile, e);
        } catch (XmlPullParserException e) {
            throw new MojoExecutionException("Error parsing POM " + pomFile, e);
        } finally {
            IOUtil.close(reader);
        }
    }

    /**
     * Generates a minimal POM from the user-supplied artifact information.
     *
     * @return The path to the generated POM file, never <code>null</code>.
     * @throws MojoExecutionException If the generation failed.
     */
    private File generatePomFile(String artifactId)
            throws MojoExecutionException {
        Model model = generateModel(artifactId);

        Writer fw = null;
        try {
            File tempFile = File.createTempFile("mvndeploy", ".pom");
            tempFile.deleteOnExit();

            fw = WriterFactory.newXmlWriter(tempFile);
            new MavenXpp3Writer().write(fw, model);

            return tempFile;
        } catch (IOException e) {
            throw new MojoExecutionException("Error writing temporary pom file: " + e.getMessage(), e);
        } finally {
            IOUtil.close(fw);
        }
    }

    /**
     * Validates the user-supplied artifact information.
     *
     * @throws MojoExecutionException If any artifact coordinate is invalid.
     */
    private void validateArtifactInformation(String artifactId)
            throws MojoExecutionException {
        Model model = generateModel(artifactId);

        ModelValidationResult result = modelValidator.validate(model);

        if (result.getMessageCount() > 0) {
            throw new MojoExecutionException("The artifact information is incomplete or not valid:\n"
                    + result.render("  "));
        }
    }

    /**
     * Generates a minimal model from the user-supplied artifact information.
     *
     * @return The generated model, never <code>null</code>.
     */
    private Model generateModel(String artifactId) {
        Model model = new Model();

        model.setModelVersion("4.0.0");

        model.setGroupId(groupId);
        model.setArtifactId(artifactId);
        model.setVersion(version);
        model.setPackaging(packaging);

        model.setDescription(description);

        return model;
    }

    private void thirdPartyJarHandle(File buildFile) {

    }

    private void analyzeBuildFile(File file) {

    }

    private void uploadJarFileToNexus(String[] filePath) {

    }

    void setGroupId(String groupId) {
        this.groupId = groupId;
    }

//    void setArtifactId(String artifactId) {
//        this.artifactId = artifactId;
//    }
    void setVersion(String version) {
        this.version = version;
    }

    void setPackaging(String packaging) {
        this.packaging = packaging;
    }

    void setPomFile(File pomFile) {
        this.pomFile = pomFile;
    }

    String getGroupId() {
        return groupId;
    }

//    String getArtifactId() {
//        return artifactId;
//    }
    String getVersion() {
        return version;
    }

    String getPackaging() {
        return packaging;
    }

//    File getFile() {
//        return file;
//    }
    String getClassifier() {
        return classifier;
    }

    void setClassifier(String classifier) {
        this.classifier = classifier;
    }

}
